package com.gitee.wsl.model.process

import com.gitee.wsl.model.Model
import com.gitee.wsl.model.ModelConfig
import com.gitee.wsl.model.process.call.ProcessClientCall
import com.gitee.wsl.model.process.plugins.ProcessSend
import com.gitee.wsl.model.process.request.ProcessRequestBuilder
import com.gitee.wsl.model.process.request.RequestPipeline
import com.gitee.wsl.model.process.request.SendPipeline
import com.gitee.wsl.model.process.response.ReceivePipeline
import com.gitee.wsl.model.process.response.ResponsePipeline
import kotlinx.coroutines.CompletableJob

class ProcessModel(applicationJob: CompletableJob,
                   private val userConfig: ProcessModelConfig = ProcessModelConfig()): Model<ProcessModel>(applicationJob) {

    /**
     * A pipeline used for processing all requests sent by this client.
     */
     val requestPipeline: RequestPipeline = RequestPipeline()

    /**
     * A pipeline used for processing all responses sent by the server.
     */
     val responsePipeline: ResponsePipeline = ResponsePipeline()

    /**
     * A pipeline used for sending a request.
     */
     val sendPipeline: SendPipeline = SendPipeline()

    /**
     * A pipeline used for receiving a request.
     */
     val receivePipeline: ReceivePipeline = ReceivePipeline()

    init {
        sendPipeline.intercept(SendPipeline.Receive) { call ->
            check(call is ProcessClientCall) { "Error: HttpClientCall expected, but found $call(${call::class})." }
            val response = receivePipeline.execute(Unit, call.processResponse)
            call.setResponse(response)
            proceedWith(call)
        }


        with(userConfig) {
            config.install(ProcessSend)

//            val coust = createProcessPlugin("dadf"){
//                this.onRequest{t,b->
//
//                }
//            }
//            config.install(coust)

            config += this
            config.install(this@ProcessModel)
        }

        responsePipeline.intercept(ResponsePipeline.Receive) {
            try {
                proceed()
            } catch (cause: Throwable) {
                monitor.raise(ProcessResponseReceiveFailed, ProcessResponseReceiveFail(context.processResponse, cause))
                throw cause
            }
        }

    }

    /**
     * Creates a new [ProcessClientCall] from a request [builder].
     */
    internal suspend fun execute(builder: ProcessRequestBuilder): ProcessClientCall {
        monitor.raise(HttpRequestCreated, builder)

        return requestPipeline.execute(builder, builder.body) as ProcessClientCall
    }

    fun config(block: ProcessModelConfig.() -> Unit) =
        ProcessModel(applicationJob,
            userConfig.clone().apply { block() }
            /*ProcessModelConfig().apply {
                    plusAssign(userConfig)
                    block()
        }*/)
}
